﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using eLuk.CommandEngine;
using eLuk.Entity;
using eLuk.MimeParser;
using System.Text.RegularExpressions;

namespace eLuk.Runtime
{
	public class IMAPBrowserState : IMAPState, IBrowsable, IDisconnectable
	{
		public IMAPBrowserState()
		{
		}
		public override void Change( IMAPSession session )
		{
			session.CurrentState = new IMAPMailboxState();
		}
		public string Create( IMAPSession session )
		{
			return "";
		}
		public string Delete( IMAPSession session )
		{
			return "";
		}
		public string Rename( IMAPSession session )
		{
			return "";
		}
		public async Task<eLukFolder> List( IMAPSession session )
		{
			eLukFolder rootFolder = new eLukFolder( "", session.Server.Credential.User );
			rootFolder.HasSubfolder = true;
			rootFolder.IsRoot = true;
			//string commandResult = await session.Engine.ExecuteAsync( imapCommands.List, "", "%" );
			await this.PrepareFolder( session, rootFolder, "* LIST (\\HasChildren) \"/\" \"\"" );

			//IMAPFolder.Append( Encoding.UTF8.GetBytes( "* LIST (\\HasChildren) \"/\" \"\"" ) );

			if ( rootFolder.SubFolders != null && rootFolder.SubFolders.Count == 1 && rootFolder.SubFolders[ 0 ].SubFolders != null )
			{
				rootFolder = rootFolder.SubFolders[ 0 ];
			}
			return rootFolder;
		}
		private async Task PrepareFolder( IMAPSession session, eLukFolder folder, string result )
		{
			string commandResult = result;
			eLukFolder currentFolder = null;
			string[] lines = commandResult.Split( new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries );
			foreach ( string line in lines )
			{
				Regex FolderParseRex = new Regex( Expressions.Folder, RegexOptions.IgnoreCase );
				Match match = FolderParseRex.Match( line );

				if ( match.Groups.Count == 4 )
				{
					currentFolder = new eLukFolder( match.Groups[ 3 ].Value );
					//folder.Name = ;
					//if ( match.Groups[ 2 ].Value == "/" )
					currentFolder.ParentFolder = folder;
					//currentFolder.ParentFolder = folder;

					currentFolder.HasSubfolder = !match.Groups[ 1 ].Value.Contains( "\\HasNoChildren" );
					currentFolder.NoSelect = match.Groups[ 1 ].Value.Contains( "\\Noselect" );
					if ( currentFolder.HasSubfolder && !currentFolder.NoSelect )
					{
						IMAPCommand list = new IMAPCommand( "LIST", "LIST \"{0}/\" {1}" );
						commandResult = await session.Engine.ExecuteAsync( list, currentFolder.Name, "%" );
						await this.PrepareFolder( session, currentFolder, commandResult );
					}
					else
					{

					}

					if ( folder.SubFolders == null )
						folder.SubFolders = new eLukFolders();

					if ( !currentFolder.NoSelect )
						folder.SubFolders.Add( currentFolder );
				}
			}
		}
		public string Select( IMAPSession session )
		{
			return "";
		}
		public async Task<string> Select( IMAPSession session, eLukFolder folder, bool iterateSubFolder )
		{
			try
			{

				if ( iterateSubFolder && folder.HasSubfolder )
				{
					for ( int i = 0; i < folder.SubFolders.Count; i++ )
					{
						if ( folder.SubFolders[ i ].HasSubfolder )
						{
							await this.Select( session, folder.SubFolders[ i ], iterateSubFolder );
						}
						IMAPCommand select = new IMAPCommand( "SELECT", "SELECT \"{0}\"" );
						string result = await session.Engine.ExecuteAsync( select, folder.SubFolders[ i ].Name );
						if ( result.Trim() != "" && result.StartsWith( "* FLAGS" ) )
						{
							this.UpdateFolderProperties( folder.SubFolders[ i ], result );
							IMAPCommand search = new IMAPCommand( "UID SEARCH", "UID SEARCH {0}" );
							result = await session.Engine.ExecuteAsync( search, "ALL" );
							await this.UpdateMessages( session, folder.SubFolders[ i ], result );
						}
					}
				}
				else
				{
					IMAPCommand select = new IMAPCommand( "SELECT", "SELECT \"{0}\"" );
					string result = await session.Engine.ExecuteAsync( select, folder.Name );
					this.UpdateSelectResult( folder, result );
					IMAPCommand search = new IMAPCommand( "UID SEARCH", "UID SEARCH {0}" );
					result = await session.Engine.ExecuteAsync( search, "ALL" );
					await this.UpdateMessages( session, folder, result );
				}
				return "";
			}
			catch ( Exception ex )
			{
				return ex.Message;
			}
		}
		private bool UpdateSelectResult( eLukFolder folder, string selectResponse )
		{
			bool result = false;

			string[] lines = selectResponse.Split( new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries );
			Regex regEx = null;
			Match match = null;
			string str = "";
			string[] strings;

			if ( lines.Length == 7 )
			{
				if ( lines[ 0 ].Contains( "* FLAGS " ) )
				{
					regEx = new Regex( Expressions.BetweenParanthesis, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 0 ] );
					if ( match.Groups.Count == 2 )
					{
						str = match.Groups[ 1 ].Value;
						strings = str.Split( " ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
						foreach ( string str1 in strings )
						{
							str = str1.Replace( "\\", "" );
							str = str.Replace( "$", "" );
							if ( Enum.GetNames( typeof( FolderFlags ) ).Contains( str ) )
							{
								if ( folder.FolderFlags == 0 )
									folder.FolderFlags = ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str );
								else
									folder.FolderFlags = folder.FolderFlags & ( ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str ) );
							}
						}
					}
				}

				if ( lines[ 1 ].Contains( "PERMANENTFLAGS" ) )
				{
					regEx = new Regex( Expressions.FolderPermanentFlags, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 1 ] );
					if ( match.Groups.Count == 2 )
					{
						str = match.Groups[ 1 ].Value;
						strings = str.Split( " ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
						foreach ( string str1 in strings )
						{
							str = str1.Replace( "\\", "" );
							str = str.Replace( "$", "" );
							if ( Enum.GetNames( typeof( FolderFlags ) ).Contains( str ) )
							{
								if ( folder.FolderFlags == 0 )
									folder.FolderFlags = ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str );
								else
									folder.FolderFlags = folder.FolderFlags & ( ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str ) );
							}
						}
					}

				}

				if ( lines[ 2 ].Contains( "UIDVALIDITY" ) )
				{
					regEx = new Regex( Expressions.FolderUIValidity, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 2 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.UIDValidatity = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 3 ].Contains( "EXISTS" ) )
				{
					regEx = new Regex( Expressions.FolderExists, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 3 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.Exists = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 4 ].Contains( "RECENT" ) )
				{
					regEx = new Regex( Expressions.FolderRecent, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 4 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.Recent = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 5 ].Contains( "UIDNEXT" ) )
				{
					regEx = new Regex( Expressions.FolderUIDNext, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 5 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.UIDNext = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 6 ].Contains( "HIGHESTMODSEQ" ) )
				{
					regEx = new Regex( Expressions.FolderHighestModSequence, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 6 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.HighestModSequence = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				//lines[0]
			}
			return result;
		}
		private async Task<bool> UpdateMessages( IMAPSession session, eLukFolder folder, string searchResult )
		{
			bool result = true;

			if ( searchResult.StartsWith( "* SEARCH " ) )
			{
				searchResult = searchResult.Replace( "* SEARCH ", "" );
				searchResult = searchResult.Replace( "\r\n", "" );
				string[] UIDs = searchResult.Split( new string[] { " " }, StringSplitOptions.RemoveEmptyEntries );
				MailMessage message = null;
				uint uuid = 0;
				foreach ( string uid in UIDs )
				{
					uuid = uint.Parse( uid );
					if ( folder.MailMessages == null )
						folder.MailMessages = new eLuk.Entity.MailMessages();
					else
						message = folder.MailMessages.Find( m => m.UID == uuid );
					if ( message == null )
					{
						message = new MailMessage();
						message.UID = uuid;
					}
					//await this.FetchEnvelope( session, message );
					await this.Fetch( session, message );
					//this.Fetch( message );
					folder.MailMessages.Add( message );
				}
			}

			return result;
		}
		public async Task<bool> Fetch( IMAPSession session, MailMessage message )
		{
			IMAPCommand fetch = null;
			string result = "";
			MimeMessage mm = null;
			try
			{
				fetch = new IMAPCommand( "UID FETCH", "UID FETCH {0} ({1})" );
				if ( message.UID == 691 )//621
				{
					;
				}
				result = await session.Engine.ExecuteAsync( fetch, message.UID, "FLAGS BODY.PEEK[] INTERNALDATE RFC822.SIZE" );
				int _startAt = result.IndexOf( "*" );
				if ( _startAt != 0 )
				{
					result = result.Substring( _startAt );
				}

				mm = new MimeMessage( result );
				this.UpdateMessage( session, message, mm );
				return true;
			}
			catch ( InvalidMimeMessageException mex )
			{
				string str = mex.Message;
				return false;
			}
			catch ( Exception ex )
			{
				string str = ex.Message;
				return false;
			}
		}
		private void UpdateMessage( IMAPSession session, MailMessage message, MimeMessage mm )
		{
			this.UpdateMessageHeader( session, message, mm.RootNode.Headers );
			message.Seen = mm.Seen;
			message.Size = mm.Size;
			List<MimeLeafNode> nodes = mm.GetAllLeafNodes().ToList();
			MessageContent mc = null;
			foreach ( MimeLeafNode node in nodes )
			{
				if ( node.ContentType == "text/plain" )
				{
					mc = new MessageContent();
					mc.ContentType.Type = node.ContentType;
					mc.ContentType.TransferEncoding = node.ContentTransferEncoding;
					//mc.ContentType.Boundry = node.Headers.FirstOrDefault( f => f.Key == "Boundary" ).Value;
					mc.Content = node.Body;
					message.MessageContents.Add( mc );
				}
				if ( node.ContentType == "text/html" )
				{
					mc = new MessageContent();
					mc.ContentType.Type = node.ContentType;
					mc.ContentType.TransferEncoding = node.ContentTransferEncoding;
					//mc.ContentType.Boundry = node.Headers.FirstOrDefault( f => f.Key == "Boundary" ).Value;
					if ( mc.ContentType.TransferEncoding == "quoted-printable" )
					{
						mc.Content = node.Body.DecodeQuotedPrintable();
					}
					else
					{
						mc.Content = node.Body;
					}
					message.MessageContents.Add( mc );
				}
				if ( !node.ContentType.StartsWith( "text/" ) )
				{

					string disposition = node.Headers.FirstOrDefault( f => f.Key == "Content-Disposition" ).Value;
					string file = disposition.Replace( "attachment; filename=", "" ).Trim( "\"".ToCharArray() );
					Attachment attachment = new Attachment( file );
					attachment.IsEmbededObject = !disposition.Contains( "attachment" );
					attachment.ContentType = new ContentType();
					attachment.ContentType.Type = node.ContentType;
					attachment.ContentType.TransferEncoding = node.ContentTransferEncoding;
					attachment.ContentType.ContentDisposition = disposition.Replace( "; filename=\"" + file + "\"", "" );

					if ( attachment.ContentType.TransferEncoding == "quoted-printable" )
					{
						attachment.Content = System.Text.Encoding.UTF8.GetBytes( node.Body );
					}
					else
					{
						attachment.Content = System.Text.Encoding.UTF8.GetBytes( node.Body );
					}

					if ( node.ContentType.StartsWith( "image/" ) )
					{
						string profilephoto = node.Headers.FirstOrDefault( f => f.Key == "Content-ID" ).Value;
						if ( profilephoto.TrimStart( '<' ).TrimEnd( '>' ) == "profilephoto" )
						{
							message.From.ProfilePictureRaw = attachment.Content;
						}
					}
					message.Attachments.Add( attachment );
				}
			}
		}
		private async void UpdateMessageHeader( IMAPSession session, MailMessage message, MimeHeaderCollection hcollection )
		{
			foreach ( MimeHeader header in hcollection )
			{
				if ( header.Key == session.MessageParserTokens.Date )
				{
					DateTime dt;
					await Misc.WriteFile( header.Value );
					bool b = DateTime.TryParseExact( header.Value, Misc.DateTimePatterns,
						System.Globalization.DateTimeFormatInfo.CurrentInfo,
						System.Globalization.DateTimeStyles.AdjustToUniversal, out dt );
					if ( b )
						message.SendReceiveDateTime = dt;
					else
						message.SendReceiveDateTime = DateTime.UtcNow;
				}
				if ( header.Key == session.MessageParserTokens.MessageID )
				{
					message.MessageID = header.Value;
				}
				if ( header.Key == session.MessageParserTokens.Subject )
				{
					message.Subject = header.Value;
				}
				if ( header.Key == session.MessageParserTokens.From )
				{
					message.From.Address = header.Value;
					message.From.DisplayName = header.Value;
				}
				if ( header.Key == session.MessageParserTokens.To )
				{
					message.To.Add( new MailAddress( header.Value, header.Value ) );
				}
			}
		}
		public async Task<string> Examine( IMAPSession session, eLukFolder folder )
		{
			try
			{
				for ( int i = 0; i < folder.SubFolders.Count; i++ )
				{
					if ( folder.SubFolders[ i ].HasSubfolder )
					{
						await this.Examine( session, folder.SubFolders[ i ] );
					}
					IMAPCommand examine = new IMAPCommand( "EXAMINE", "EXAMINE \"{0}\"" );
					string result = await session.Engine.ExecuteAsync( examine, folder.SubFolders[ i ].Name );
					this.UpdateFolderProperties( folder.SubFolders[ i ], result );
				}
				return "";
			}
			catch ( Exception ex )
			{
				return ex.Message;
			}
		}
		private bool UpdateFolderProperties( eLukFolder folder, string examinResponse )
		{
			bool result = false;

			string[] lines = examinResponse.Split( new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries );
			Regex regEx = null;
			Match match = null;
			string str = "";
			string[] strings;

			if ( lines.Length == 7 )
			{
				if ( lines[ 0 ].Contains( "* FLAGS " ) )
				{
					regEx = new Regex( Expressions.BetweenParanthesis, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 0 ] );
					folder.FolderFlags = eLuk.Entity.FolderFlags.Answered;
					if ( match.Groups.Count == 2 )
					{
						str = match.Groups[ 1 ].Value;
						strings = str.Split( " ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
						foreach ( string str1 in strings )
						{
							str = str1.Replace( "\\", "" );
							str = str.Replace( "$", "" );
							if ( Enum.GetNames( typeof( FolderFlags ) ).Contains( str ) )
							{
								if ( folder.FolderFlags == 0 )
									folder.FolderFlags = ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str );
								else
									folder.FolderFlags = folder.FolderFlags & ( ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str ) );
							}
						}
					}
				}

				if ( lines[ 1 ].Contains( "PERMANENTFLAGS" ) )
				{
					regEx = new Regex( Expressions.FolderPermanentFlags, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 1 ] );
					if ( match.Groups.Count == 2 )
					{
						str = match.Groups[ 1 ].Value;
						strings = str.Split( " ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
						foreach ( string str1 in strings )
						{
							str = str1.Replace( "\\", "" );
							str = str.Replace( "$", "" );
							if ( Enum.GetNames( typeof( FolderFlags ) ).Contains( str ) )
							{
								if ( folder.FolderFlags == 0 )
									folder.FolderFlags = ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str );
								else
									folder.FolderFlags = folder.FolderFlags & ( ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str ) );
							}
						}
					}

				}

				if ( lines[ 2 ].Contains( "UIDVALIDITY" ) )
				{
					regEx = new Regex( Expressions.FolderUIValidity, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 2 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.UIDValidatity = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 3 ].Contains( "EXISTS" ) )
				{
					regEx = new Regex( Expressions.FolderExists, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 3 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.Exists = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 4 ].Contains( "RECENT" ) )
				{
					regEx = new Regex( Expressions.FolderRecent, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 4 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.Recent = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 5 ].Contains( "UIDNEXT" ) )
				{
					regEx = new Regex( Expressions.FolderUIDNext, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 5 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.UIDNext = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 6 ].Contains( "HIGHESTMODSEQ" ) )
				{
					regEx = new Regex( Expressions.FolderHighestModSequence, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 6 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.HighestModSequence = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				//lines[0]
			}
			return result;
		}
		public string Disconnect( IMAPSession session )
		{
			return "";
		}

	}
}


